package com.cfh.springcloudserver.aop;

import com.cfh.springcloudserver.annotation.SemaphoreCircuitBreaker;
import com.cfh.springcloudserver.annotation.TimeoutCircuitBreaker;
import com.cfh.springcloudserver.controller.ServerController;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.util.concurrent.*;

/**
 * {@link ServerController} 切面
 */
@Aspect
@Component
public class ServerControllerAspect {

    ExecutorService executorService = Executors.newFixedThreadPool(20);

    private Semaphore semaphore;

    @Around("execution(* com.cfh.springcloudserver.controller.ServerController.advancedSay(..))" +
            "&&args(messsage) ")
    public Object advancedSayTimeout(ProceedingJoinPoint joinPoint, String messsage) {
        return doInvoke(joinPoint, messsage, 100);
    }


    @Around("execution(* com.cfh.springcloudserver.controller.ServerController.finalSay(..))" +
            "&&args(messsage)&& @annotation(circuitBreaker)")
    public Object finalSayTimeout(ProceedingJoinPoint joinPoint, String messsage,
                                  TimeoutCircuitBreaker circuitBreaker) {
        long timeout = circuitBreaker.timeout();
        return doInvoke(joinPoint, messsage, timeout);
    }

    @Around("execution(* com.cfh.springcloudserver.controller.ServerController.finalSay(..))" +
            "&&args(messsage)&& @annotation(circuitBreaker)")
    public Object final2SayTimeout(ProceedingJoinPoint joinPoint, String messsage,
                                   SemaphoreCircuitBreaker circuitBreaker) throws
            Throwable {
        int value = circuitBreaker.value();
        if (semaphore == null) {
            semaphore = new Semaphore(value);
        }
        Object proceed = null;
        try {
            if (semaphore.tryAcquire()) {
                proceed = joinPoint.proceed(new String[]{messsage});
            } else {
                proceed = "Default";
            }
        } finally {
            semaphore.release();
        }
        return proceed;
    }


    @PreDestroy
    public void destroy() {
        executorService.shutdown();
    }


    private Object doInvoke(ProceedingJoinPoint joinPoint, String messsage, long timeout) {
        Future<Object> future = executorService.submit(() -> {
            Object proceed = null;
            try {
                proceed = joinPoint.proceed(new String[]{messsage});
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            return proceed;
        });

        Object returnValue = null;
        try {
            returnValue = future.get(timeout, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            future.cancel(true);
            returnValue = "Default";
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return returnValue;
    }
}
